
#include "../iface/debug.h"
#include "XMLReader.h"
#include "XMLWriter.h"
#include "DOMDocument.h"

/** Read in a document to store in this instance from a buffer.
 * 
 * @param buffer
 * Buffer holding the XML document text.
 * 
 * @param length
 * Length of the buffered text.
 * 
 * @return
 * Zero on success, non-zero on failure. */
int DOMDocument::readIn(const char *buffer, int length)
{
   XMLReader xmlreader;
   int wassuccess = 0;
   int wasvsuccess = 0;
//   tdoc = xmlreader.XMLRead(filename, &wassuccess, &wasvsuccess);
   // Don't validate!
   if (tdoc) xmlFreeDoc(tdoc);
   tdoc = xmlreader.xmlRead(buffer, length, &wassuccess, NULL);
   if (!wassuccess)
   {
      errprint("failed to parse buffer");
      tdoc = xmlNewDoc(BAD_CAST "1.0");
      return 1;
   }
   return 0;
}

/** Read in a document to store in this instance from a file.
 * 
 * @param filename
 * The name of the file to open and read the XML document from.
 * 
 * @return
 * Zero on success, non-zero in failure. */
int DOMDocument::readIn(const char *filename)
{
   XMLReader xmlreader;
   int wassuccess = 0;
   int wasvsuccess = 0;
//   tdoc = xmlreader.XMLRead(filename, &wassuccess, &wasvsuccess);
   // Don't validate!
   if (tdoc) xmlFreeDoc(tdoc);
   tdoc = xmlreader.xmlRead(filename, &wassuccess, NULL);
   if (!wassuccess)
   {
      errprint("failed to parse file '%s'", filename);
      tdoc = xmlNewDoc(BAD_CAST "1.0");
      return 1;
   }
   //if (!wasvsuccess) errprint("failed to validate file '%s'", filename);
   return 0;
}

/** Constructor for the class. Reads in an XML document from a buffer.
 * 
 * @param buffer
 * Buffer holding the XML document text.
 * 
 * @param length
 * Length of the buffered text. */
DOMDocument::DOMDocument(const char *buffer, int length)
{
   tdoc = NULL;
   readIn(buffer, length);
}

/** Constructor for the class. Read in an XML document from a file.
 * 
 * @param filename
 * The name of the file to open and read the XML document from.
 * 
 * @return
 * Zero on success, non-zero in failure. */
DOMDocument::DOMDocument(const char *filename)
{
   tdoc = NULL;
   readIn(filename);
}

/** Constructor for the class that generates a blank document. */
DOMDocument::DOMDocument()
{
   tdoc = xmlNewDoc(BAD_CAST "1.0");
}

/** Destructor for the XML document representation. */
DOMDocument::~DOMDocument()
{
   xmlFreeDoc(tdoc);
}

/** Get the root node of the document in the DOM tree format. */
xmlNodePtr DOMDocument::getRootNode()
{
   return xmlDocGetRootElement(tdoc);
}

/** Generate a root node for a blank document with the given name.
 * 
 * @param name
 * The name of the root node to be generated in this blank document. */
void DOMDocument::generateRootNode(const char *name)
{
   xmlNodePtr n = xmlNewNode(NULL, BAD_CAST name);
   xmlDocSetRootElement(tdoc, n);
}

/** Sets an attribute of an XML node.
 * 
 * @param node
 * The node (tag) of the XML tree to be changed.
 * 
 * @param key
 * The key variable name of the attribute to change.
 * 
 * @param value
 * The value of the attribute that needs to be modified. */
void DOMDocument::setProperty(xmlNodePtr node, const char *key, const char *value)
{
   xmlNewProp(node, BAD_CAST key, BAD_CAST value);
}

/** Create a child node (tag) directly inside the current node (between
 * the open and close tags).
 * 
 * @param node
 * The node that this node is to be created inside/as a child of.
 * 
 * @param name
 * The name of the new node/tag pair.
 * 
 * @return
 * A pointer to the new child node. */
xmlNodePtr DOMDocument::createChildNode(xmlNodePtr node, const char *name)
{
   return xmlNewChild(node, NULL, BAD_CAST name, NULL);
}

/** Find the node with the given name.
 * 
 * @param node
 * The pointer to the root node to search from. Only subtrees of this
 * node are considered.
 * 
 * @param name
 * Name of the node to find.
 * 
 * @return
 * Returns the node found if any, otherwise NULL if no node is found. */
xmlNodePtr DOMDocument::findNode(xmlNodePtr node, const char *name)
{
   if (strcmp((const char *)node->name, name) == 0) return node;
   if (node->children)
   {
      xmlNodePtr anychildren = findNode(node->children, name);
      if (anychildren != NULL) return anychildren;
   }
   if (node->next)
   {
      xmlNodePtr anynext = findNode(node->next, name);
      if (anynext != NULL) return anynext;
   }
   return NULL;
}

/** Find a node with the given name, attribute and attribute value.
 * 
 * 
 * @param node
 * The pointer to the root node to search from. Only subtrees of this
 * node are considered.
 * 
 * @param name
 * Name of the node to find.
 * 
 * @param attribname
 * Name of the attribute that must exist under a given candidate.
 * 
 * @param attribvalue
 * The value of the attribute under the node, which must also match.
 * 
 * @return
 * Returns the node found if any, otherwise NULL if no node is found. */
xmlNodePtr DOMDocument::findNode(xmlNodePtr node, const char *name, const char *attribname, const char *attribvalue)
{
   if (strcmp((const char *)node->name, name) == 0)
   {
      if (xmlHasProp(node, BAD_CAST attribname) != NULL)
      {
         xmlChar *valstring = xmlGetProp(node, BAD_CAST attribname);
         if (strcmp((const char *)valstring, attribvalue) == 0)
         {
            xmlFree(valstring);
            return node;
         } else xmlFree(valstring);
      }
   }
   if (node->children)
   {
      xmlNodePtr anychildren = findNode(node->children, name);
      if (anychildren != NULL) return anychildren;
   }
   if (node->next)
   {
      xmlNodePtr anynext = findNode(node->next, name);
      if (anynext != NULL) return anynext;
   }
   return NULL;
}

/** Determines whether a given node has an attribute with the given name.
 * 
 * @param node
 * The node to be checked for the attribute.
 * 
 * @param attribname
 * The attribute name to be found.
 * 
 * @return
 * Returns the node found if any, otherwise NULL if no node is found. */
int DOMDocument::hasAttrib(xmlNodePtr node, const char *attribname)
{
   return (xmlHasProp(node, BAD_CAST attribname) != NULL);
}

/** Gets a string corresponding to the value of a given attribute
 * found under a given node. The caller assumes responsibility for
 * freeing the string pointer.
 * 
 * @param node
 * The node to be checked for the attribute.
 * 
 * @param attribname
 * The attribute name to be found.
 * 
 * @return
 * Returns the node found if any, otherwise NULL if no node is found. */
char *DOMDocument::getAttribValueString(xmlNodePtr node, const char *attribname)
{
   xmlChar *attribval = xmlGetProp(node, BAD_CAST attribname);
   if (attribval == NULL) return NULL;
   int len = xmlStrlen(attribval);
   char *attribcval = (char *)malloc(sizeof(xmlChar)*(len+1));
   memcpy(attribcval, attribval, sizeof(xmlChar)*(len+1));
   xmlFree(attribval);
   return attribcval;
}

/** Writes out the document embedded in this instance to a file.
 * 
 * @param filename
 * The file to write the document to.
 * 
 * @return
 * Zero if successful, non-zero on error. */
int DOMDocument::writeOut(const char *filename)
{
   XMLWriter xmlwriter;
   return xmlwriter.xmlWrite(filename, tdoc);
}

/** Writes out the document embedded in this instance to an XML buffer object.
 * 
 * @param buffer
 * The XML buffer to write the document to.
 * 
 * @return
 * Zero if successful, non-zero on error. */
int DOMDocument::writeOut(xmlBufferPtr buffer)
{
   XMLWriter xmlwriter;
   return xmlwriter.xmlWrite(buffer, tdoc);
}
